#include "EpollPoller.h"
#include <cstddef>
#include <cstdint>
#include <map>
#include <poll.h>
#include <sstream>
#include <unistd.h>
#include "Channel.h"
#include "DateTime.h"
#include "PlatLogger.h"
#if defined(__CYGWIN__)
#else
#    include <sys/epoll.h>
#endif
#include "Util.h"
namespace xncc {
namespace foundation {
    EpollPoller::EpollPoller()
    {
#if defined(__CYGWIN__)
        throw logic_exception("CYGWIN system not support epoll");
#else
        epollfd_ = ::epoll_create1(EPOLL_CLOEXEC);
        events_  = EventList(16);
#endif
    }
    EpollPoller::~EpollPoller()
    {
#if defined(__CYGWIN__)
#else
        ::close(epollfd_);
#endif
    }

    void EpollPoller::addChannel(const std::shared_ptr<Channel>& chnl)
    {
#if defined(__CYGWIN__)
#else
        Poller::addChannel(chnl);
        int fd        = chnl->fd();
        channels_[fd] = chnl;
        update(EPOLL_CTL_ADD, chnl);
#endif
    }
    void EpollPoller::removeChannel(const std::shared_ptr<Channel>& chnl)
    {
#if defined(__CYGWIN__)
#else
        Poller::removeChannel(chnl);
        int fd = chnl->fd();
        if (unlikely(channels_.find(fd) == channels_.end())) {
            std::ostringstream oss;
            oss << "remove channel of fd = " << fd << ", but channel not exist";
            throw shared::logic_exception(oss.str());
        }
        assert(channels_[fd] == chnl);
        size_t n = channels_.erase(fd);
        (void)n;
        assert(n == 1);
        update(EPOLL_CTL_DEL, chnl);
#endif
    }
    void EpollPoller::updateChannel(const std::shared_ptr<Channel>& chnl)
    {
#if defined(__CYGWIN__)
#else
        // 使用裸指针 update如果是pending函数，有可能 ch已经因为断开而被移除和析构了
        // 存在崩溃风险，调整为智能指针的原因
        int fd = chnl->fd();
        if (unlikely(channels_.find(fd) == channels_.end())) {
            TRACE_ERROR << "channel may be remove because of disconnect "_s << fd;
            return;
        }
        assert(channels_[fd] == chnl);
        update(EPOLL_CTL_MOD, chnl);
#endif
    }
    void EpollPoller::loopOnce(int waitUs)
    {
#if defined(__CYGWIN__)
#else
        int numEvents  = ::epoll_wait(epollfd_, &*events_.begin(), static_cast<int>(events_.size()),
                                      waitUs / types::DateTime::MICROS_PER_MILL);
        int savedErrno = errno;
        if (numEvents > 0) {
            for (std::size_t i = 0; i < static_cast<std::size_t>(numEvents); ++i) {
                // epoll_wait 阻塞返回的时候，拿到这个指针，也不使用智能指针，保证引用技术正确
                auto*    channel = static_cast<Channel*>(events_[i].data.ptr);
                uint32_t events  = events_[i].events;
                if (channel == nullptr) {
                    continue;
                }
                const auto        fd     = channel->fd();
                const auto        connId = channel->connId();
                const auto* const type   = ChannelType::toString(channel->Type());
                if ((events & (POLLIN | POLLPRI | POLLRDHUP)) != 0U) {
                    channel->handleRead();  // 回调函数可能执行删除channel的操作 导致后续的操作失效
                }
                if ((events & POLLHUP) && !(events & POLLIN)) {  // UDP socket 不会触发POLLHUP
                    if (channels_.find(fd) != channels_.end()) {
                        channel->handleClose(shared::getDisconnectedReason(
                            savedErrno));  // 回调函数可能执行删除channel的操作 导致后续的操作失效
                    }
                    else {
                        TRACE_ERROR << "channel not exists POLLHUP channel:"_s << connId << " fd:"_s << fd
                                    << " channel_type:" << type;
                    }
                }
                if ((events & POLLNVAL) != 0U) {}
                if ((events & (POLLERR | POLLNVAL)) != 0U) {  // UDP socket ICMP错误 触发 POLLERR
                    if (channels_.find(fd) != channels_.end()) {
                        TRACE_ERROR << "POLLERR or POLLNVAL channel:"_s << connId << " fd:"_s << fd
                                    << " channel_type:" << type;
                        channel->handleError(shared::getDisconnectedReason(
                            savedErrno));  // 回调函数可能执行删除channel的操作 导致后续的操作失效
                    }
                    else {
                        TRACE_ERROR << "channel not exists POLLERR or POLLNVAL channel:"_s << connId << " fd:"_s << fd
                                    << " channel_type:" << type;
                    }
                }
                if ((events & POLLOUT) != 0U) {
                    if (channels_.find(fd) != channels_.end()) {
                        channel->handleWrite();
                    }
                    else {
                        TRACE_ERROR << "channel not exists POLLOUT channel:"_s << connId << " fd:"_s << fd
                                    << " channel_type:" << type;
                    }
                }
            }
            if (static_cast<size_t>(numEvents) == events_.size()) {
                events_.resize(events_.size() * 2);
            }
        }
        else if (numEvents == 0) {
        }
        else {
            if (savedErrno != EINTR) {
                errno = savedErrno;
                SYSTEM_ERROR << "EPollPoller::poll()"_s;
            }
        }
#endif
    }

    void EpollPoller::update(int operation, const std::shared_ptr<Channel>& chnl) const
    {
#if defined(__CYGWIN__)
#else
        struct epoll_event event = {};
        shared::memZero(event);
        event.events = static_cast<uint32_t>(chnl->events());
        // 使用的原始指针，所以这个拷贝动作，没有增加引用计数
        //  epoll_wait 阻塞返回的时候，拿到这个指针，也不使用智能指针，保证引用技术正确
        event.data.ptr = chnl.get();
        int fd         = chnl->fd();
        if (::epoll_ctl(epollfd_, operation, fd, &event) < 0) {
            SYSTEM_ERROR << "epoll ctl failed"_s;
        }
#endif
    }

}  // namespace foundation
}  // namespace xncc